#ifndef ILLIB_LINKEDLIST_H_
#define ILLIB_LINKEDLIST_H_

#include <illib/BasicLinkedList.h>
#include <illib/Allocator.h>
#include <illib/System.h>


namespace illib{
	namespace LinkedList{
		template<typename T> class List;
		template<typename T> class Iterator;
		template<class T> class Node : public BasicLinkedList::Node, public T{
		public:
			typedef T ElementType;
			typedef List<T> ListType;
			typedef Iterator<T> IteratorType;
			typedef Node<T> NodeType;
			typedef T Type;
		private:
			Node(const NodeType &other);
		protected:
			friend class List<T>;
			friend class Iterator<T>;
		public:
			Node() :
				BasicLinkedList::Node()
			{

			}
			static NodeType & create(Allocator & allocator, size_t size){
				size_t allocatedSize;
				uint8_t * memory = allocator.allocate(size, allocatedSize);
				if(reinterpret_cast<uintptr_t>(memory) % alignof(T)) throw Exceptions::alignment;
				NodeType & node = *new(memory) NodeType();
				return node;
			}
			void destroy(){
				this->~Node();
				assert(MemoryTracker::tracker().release(this));
			}
		};

		template<class T> class Iterator : public BasicLinkedList::Iterator{
		public:
			typedef List<T> ListType;
			typedef Iterator<T> IteratorType;
			typedef Node<T> NodeType;
			typedef T ElementType;
		private:
			Iterator(const IteratorType &other);
			Iterator();
		public:
			Iterator(ListType &list, bool end = false) :
				BasicLinkedList::Iterator(list, end)
			{

			}
			NodeType * current(){
				if (!current_) return 0;
				return static_cast<NodeType*>(current_);
			}
		};

		template<class T> class List : public BasicLinkedList::List{
		public:
			typedef List<T> ListType;
			typedef Iterator<T> IteratorType;
			typedef Node<T> NodeType;
			typedef T ElementType;
		private:
			List(const List &other);
		public:
			List() : BasicLinkedList::List(){

			}
			~List(){
			}
			void clear(Allocator *allocator){
				while(length()){
					NodeType * node = tail();
					remove(length()-1);
					MemoryTracker::Tracked * entry = MemoryTracker::tracker().entry(node);
					node->~NodeType();
					if(!entry) continue;
					Allocator * root = entry->allocator()->root();
					if(!root) continue;
					if(allocator == 0 || root == allocator) assert(entry->allocator()->release(node));
				}
			}
			void clear(){
				BasicLinkedList::List::clear();
			}
			NodeType* head() {
				if(!length()) return 0;
				return static_cast<NodeType*>(BasicLinkedList::List::head());
			}
			NodeType* tail() {
				if(!length()) return 0;
				return static_cast<NodeType*>(BasicLinkedList::List::tail());
			}
			NodeType* add(size_t index, Allocator & allocator, size_t size = sizeof(NodeType)){
				if (index > length_)
					return 0;
				NodeType & newNode = NodeType::create(allocator, size);
				BasicLinkedList::List::add(newNode, index);
				return &newNode;
			}
			NodeType* add(NodeType & node, size_t index){
				if (index > length_)
					return 0;
				BasicLinkedList::List::add(node, index);
				return node;
			}
			NodeType * remove(size_t index){
				NodeType * node = static_cast<NodeType*>(BasicLinkedList::List::remove(index));
				if(node) {
					return node;
				}
				return 0;
			}
			NodeType * get(size_t index){
				NodeType * node =  static_cast<NodeType*>(BasicLinkedList::List::get(index));
				if(node) return node;
				return 0;
			}
		};
	};
};
#endif
